package com.wonderful.compose.state

import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel

/**
 * Desc: state of compose
 * @Author: wonderful
 * @Time: 2023/11/15 15:27
 */

@Composable
fun Counter(modifier: Modifier) {
    // remember 和 mutableStateOf 在 Composable 函数中几乎永远都是配套使用的
    // remember 函数的作用是让其包裹住的变量在重组的过程中得到保留，从而就不会出现变量被重新初始化的情况了
    val count = remember {
        mutableStateOf(0)
    }
    Column(
        modifier = modifier,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "${count.value}", fontSize = 50.sp)
        Button(onClick = { count.value++ }) {
            Text(text = "Click me", fontSize = 26.sp)
        }
    }
}

/**
 * 借助 Kotlin 的委托语法对来 State 的用法对上面函数进一步精简
 */
@Composable
fun Counter2(modifier: Modifier) {
    var count by remember { mutableStateOf(0) }
    // 若想在横竖屏切换时保留数据，可使用 remember 的增强版 rememberSaveable
    val count2 = rememberSaveable { mutableStateOf(0) }

    Column(
        modifier = modifier,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "$count", fontSize = 50.sp)
        Button(onClick = { count++ }) {
            Text(text = "Click me", fontSize = 26.sp)
        }
    }
}

/**
 * 有状态的函数：count 是由 State 对象赋值的
 */
@Composable
fun StatefulCounter(modifier: Modifier = Modifier) {
    val count by remember { mutableStateOf(0) }
    Text(text = "$count")
}

/**
 * 无状态的函数：count 是传递进来的参数
 */
@Composable
fun StatelessCounter(count: Int, modifier: Modifier = Modifier) {
    Text(text = "$count")
}

// tip: 根据 Google 给出的最佳实践准则，有状态的 Composable 函数通常在复用性和可测试性方面都会表现得比较差。
// tip: 因此，当编写 Composable 函数时，最理想的情况就是尽可能地让它成为一个无状态的 Composable 函数。
// tip：如何做？Compose 提供了一种编程模式，叫 State hoisting，即状态提升。
// tip：也就是说，要尽可能地把 State 提到更上一层的 Composable 函数当中，这样偏底层的 Composable 函数就可以成为无状态的函数，从而提高它们的复用性。
// 步骤1：将原来声明 State 对象的写法改成用参数传递的写法，就像上面的示例一样。
// 步骤2：将写入 State 数据的地方改成用回调的方式来通知到上一层。Kotlin 语言可以借助高阶函数来轻松实现回调编写。

/**
 * 通过状态提升来优化
 */
@Composable
fun Counter3(count: Int, onIncrement: () -> Unit, modifier: Modifier = Modifier) {
    Column(
        modifier = modifier,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "$count", fontSize = 50.sp)
        Button(onClick = { onIncrement() }) {
            Text(text = "Click me", fontSize = 26.sp)
        }
    }
}

/**
 * 通过状态提升来优化后，方便复用
 */
@Composable
fun CallCounter(modifier: Modifier = Modifier) {
    var count by rememberSaveable { mutableStateOf(0) }
    var doubleCount by rememberSaveable { mutableStateOf(0) }
    Column {
        Counter3(count = count, onIncrement = { count++ }, modifier)
        Counter3(count = doubleCount, onIncrement = { doubleCount += 2 }, modifier)
    }
}

// tip：像这种状态向下传递、事件向上传递的编程模式，称之为单向数据流模式（Unidirectional Data Flow）。
// tip：而状态提升就是这种单向数据流模式在 Compose 中的具体应用。
// tip：状态提升最少应该到达哪个层级的关键因素：
// 1.如果有多个 Composable 函数需要读取同一个 State 对象，那么至少要将 State 提升到这些 Composable 函数共有的父级函数当中。
// 2.如果有多个 Composable 函数需要对同一个 State 对象进行写入，那么至少要将 State 提升到所有执行写入的 Composable 函数里调用层级最高的那一层。
// 3.如果某个事件的触发会导致两个或更多的 State 发生变更，那么这些 State 都应该提升到相同的层级。

/**
 * 引入 viewModel 和 LiveData
 */
@Composable
fun CallCounter2(
    modifier: Modifier = Modifier,
    mViewModel: CounterLiveDataViewModel = viewModel()
) {
    // 这里需要将 LiveData 转换成 State，observeAsState()函数就是用来做这个事情的
    val count by mViewModel.count.observeAsState(initial = 0)
    val doubleCount by mViewModel.doubleCount.observeAsState(0)
    Column {
        Counter3(count = count, onIncrement = { mViewModel.incrementCount() }, modifier)
        Counter3(count = doubleCount, onIncrement = { mViewModel.incrementDoubleCount() }, modifier)
    }
}

/**
 * 引入 viewModel 和 Flow
 */
@Composable
fun CallCounter3(modifier: Modifier = Modifier, mViewModel: CounterFlowViewModel = viewModel()) {
    // 这里需要将 Flow 转换成 State，collectAsState()函数就是用来做这个事情的
    val count by mViewModel.count.collectAsState()
    val doubleCount by mViewModel.doubleCount.collectAsState()
    Column {
        Counter3(count = count, onIncrement = { mViewModel.incrementCount() }, modifier)
        Counter3(count = doubleCount, onIncrement = { mViewModel.incrementDoubleCount() }, modifier)
    }
}

@Preview(showBackground = true)
@Composable
fun CounterPreview() {
    //Counter2(modifier = Modifier)
    //CallCounter()
    //CallCounter2()
    CallCounter3()
}

/**
 * 输入框demo
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditTextWidget(value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier) {
    TextField(modifier = modifier, value = value, onValueChange = onValueChange)
}

@Composable
fun EditTextDemo() {
    var userInput by remember {
        mutableStateOf("")
    }
    Column {
        EditTextWidget(value = userInput, onValueChange = {
            userInput = it
        })
    }
}

@Preview(showBackground = true)
@Composable
fun EditTextPreview() {
    EditTextDemo()
}
